#include <asm.h>
#include <csr.h>

.macro SAVE_CONTEXT
  .local _restore_kernel_tpsp
  .local _save_context
  /*
   * If coming from userspace, preserve the user thread pointer and load
   * the kernel thread pointer.  If we came from the kernel, sscratch
   * will contain 0, and we should continue on the current TP.
   */
  csrrw tp, CSR_SSCRATCH, tp
  bnez tp, _save_context

_restore_kernel_tpsp:
  csrr tp, CSR_SSCRATCH
  sd sp, PCB_KERNEL_SP(tp)
_save_context:
  sd sp, PCB_USER_SP(tp)
  ld sp, PCB_KERNEL_SP(tp)
  addi sp, sp, -(OFFSET_SIZE)

  /* TODO: save all general purpose registers here! */

  /*
   * Disable user-mode memory access as it should only be set in the
   * actual user copy routines.
   *
   * Disable the FPU to detect illegal usage of floating point in kernel
   * space.
   */
  li t0, SR_SUM | SR_FS

  /* TODO: save sstatus, sepc, stval, scause and sscratch on user stack */
.endm

.macro RESTORE_CONTEXT
  /* TODO: Restore all registers and sepc,sstatus */
.endm

ENTRY(enable_preempt)
  ld t1, current_running
  ld t0, PCB_PREEMPT_COUNT(t1)
  beq t0, zero, do_enable
  addi t0, t0, -1
  sd t0, PCB_PREEMPT_COUNT(t1)
  beq t0, zero, do_enable
  jr ra
do_enable:
  not t0, x0
  csrs CSR_SIE, t0
  jr ra
ENDPROC(enable_preempt)

ENTRY(disable_preempt)
  csrw CSR_SIE, zero
  ld t1, current_running
  ld t0, PCB_PREEMPT_COUNT(t1)
  addi t0, t0, 1
  sd t0, PCB_PREEMPT_COUNT(t1)
  jr ra
ENDPROC(disable_preempt)

ENTRY(enable_interrupt)
  li t0, SR_SIE
  csrs CSR_SSTATUS, t0
  jr ra
ENDPROC(enable_interrupt)

ENTRY(disable_interrupt)
  li t0, SR_SIE
  csrs CSR_SSTATUS, t0
  jr ra
ENDPROC(disable_interrupt)

// the address of previous pcb in a0
// the address of next pcb in a1  ！！！！！！！！这里非常重要，不能忽略。！！！！！！！！！！！！！！符合汇编函数的约定习惯
ENTRY(switch_to)
  // save all callee save registers on kernel stack
  //addi sp, sp, -(SWITCH_TO_SIZE)
  /* TODO: store all callee save registers,
   * see the definition of `struct switchto_context` in sched.h
   这里我们不再使用sp指针来存储地址，因为保存现场要保存sp的值到kerne栈，这里把值改掉还保存啥啊。干脆使用t1得了*/
  
  

   ld t1,0(a0)
   addi t1,t1,-(OFFSET_SIZE)
   addi t1,t1,-(SWITCH_TO_SIZE)
   sd ra,0(t1)
   sd sp,8(t1)
   sd s0,16(t1)
   sd s1,24(t1)
   sd s2,32(t1)
   sd s3,40(t1)
   sd s4,48(t1)
   sd s5,56(t1)
   sd s6,64(t1)
   sd s7,72(t1)
   sd s8,80(t1)
   sd s9,88(t1)
   sd s10,96(t1)
   sd s11,104(t1)


   ld t1,0(a1)
   addi t1,t1,-(OFFSET_SIZE)
   addi t1,t1,-(SWITCH_TO_SIZE)
   ld ra,0(t1)
   ld sp,8(t1)
   ld s0,16(t1)
   ld s1,24(t1)
   ld s2,32(t1)
   ld s3,40(t1)
   ld s4,48(t1)
   ld s5,56(t1)
   ld s6,64(t1)
   ld s7,72(t1)
   ld s8,80(t1)
   ld s9,88(t1)
   ld s10,96(t1)
   ld s11,104(t1)

   add tp,zero,a1
   //确保tp寄存器的值等于current_running即正在运行pcb的地址，这里就是a1的值，因为a1是switch_to的下一个进程，也就是切换结束的curren

  // restore next
  /* TODO: restore all callee save registers,
   * see the definition of `struct switchto_context` in sched.h*/

 // addi sp, sp, SWITCH_TO_SIZE
  jr ra//这里移交程序的控制权到下一个进程。这里是do_scheduler函数的最重要功能。
ENDPROC(switch_to)

ENTRY(ret_from_exception)
  /* TODO: */
ENDPROC(ret_from_exception)

ENTRY(exception_handler_entry)
  SAVE_CONTEXT

  csrw CSR_SSCRATCH, x0

  /* Load the global pointer */
  .option push
  .option norelax
  la gp, __global_pointer$
  .option pop

  /* TODO: load ret_from_exception into $ra
   * so that we can return to ret_from_exception
   * when interrupt_help complete.
   */

  /* TODO: call interrupt_helper
   * note: don't forget to pass parameters for it.
   */
ENDPROC(exception_handler_entry)
